package com.tecklab.demo.aspect;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.tecklab.demo.entity.OperationLogEntity;
import com.tecklab.demo.entity.UserEntity;
import com.tecklab.demo.mapper.OperationLogMapper;
import com.tecklab.demo.param.LoginRequest;
import com.tecklab.demo.utils.IpUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Aspect
@Component
public class OperationLogAspect {

    private final ExpressionParser parser = new SpelExpressionParser();

    @Resource
    private OperationLogMapper operationLogMapper;

    @AfterReturning(pointcut = "@annotation(operationLog)", returning = "result")
    public void doAfter(JoinPoint joinPoint, OperateLog operationLog, Object result) {

    }

    @Before("@annotation(operationLog)")
    public void doBefore(JoinPoint joinPoint, OperateLog operationLog) {
        // 获取当前请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String username = null;
        if (request.getRequestURI().contains("login")) {
            LoginRequest loginRequest = (LoginRequest) joinPoint.getArgs()[0];
            username = loginRequest.getUsername();
        } else {
            username = getUsernameByToken();
        }

        // 记录操作日志
        OperationLogEntity log = new OperationLogEntity();
        log.setOperation(operationLog.optName());
        log.setOperationType(operationLog.optType());
        log.setOperatorName(username);
        log.setOperatorIp(IpUtils.getIpByRequest());
        // 解析 SpEL 模板
        String paramsEL = operationLog.params();
        String params = null;
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        String[] paramNames = signature.getParameterNames();
        if (StringUtils.isNoneBlank(paramsEL)) {
            params = parseSpel(paramsEL, paramNames, method, args);
        } else {
            String requestMethod = request.getMethod();
            params = request.getQueryString();
            if (StringUtils.isBlank(params) && !"GET".equalsIgnoreCase(requestMethod)) {
                List<String> paramList = new ArrayList<>();
                if (args != null) {
                    for (int i = 0; i < args.length; i++) {
                        String paramName = (paramNames != null && i < paramNames.length) ? paramNames[i] : "arg" + i;
                        String valueStr = args[i] == null ? "null" : JSON.toJSONString(args[i]);
                        paramList.add(paramName + "=" + valueStr);
                    }
                }
                params = StringUtils.join(paramList, ",");
            }
        }
        if (StringUtils.isNotBlank(params) && params.length() > 200) {
            params = params.substring(0, 200);
        }
        log.setParams(params);
        //
        operationLogMapper.insert(log);

    }

    private String parseSpel(String paramsEL, String[] paramNames, Method method, Object[] args) {
        // 创建 SpEL 上下文
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }

        try {
            Class<?>[] parameterTypes = method.getParameterTypes();
            Object value = parser.parseExpression(paramsEL).getValue(context, parameterTypes[0]);
            return JSON.toJSONString(value);
        } catch (Exception e) {
            e.printStackTrace();
            return "【解析失败: " + paramsEL + "】";
        }
    }


    private String getUsernameByToken() {
        SaSession session = StpUtil.getTokenSession();
        Object object = session.get("user");
        if (object == null) {
            return "Unknown";
        }
        UserEntity user = (UserEntity) StpUtil.getTokenSession().get("user");
        return user.getUsername();
    }
}